package de.ryancarmon.validator;

import de.ryancarmon.validator.cfg.ConfigValue;
import de.ryancarmon.validator.cfg.ValidatorConfig;
import de.ryancarmon.validator.exc.CastingFailedException;
import de.ryancarmon.validator.exc.ValidationException;
import de.ryancarmon.validator.validators.BaseValidator;
import de.ryancarmon.validator.validators.DoubleValidator;
import de.ryancarmon.validator.validators.IntegerValidator;
import de.ryancarmon.validator.validators.ObjectValidator;
import de.ryancarmon.validator.validators.StringValidator;

public class Validator {
	private static Validator instance;

	private IntegerValidator valInt;
	private DoubleValidator valDouble;
	private StringValidator valString;
	private ObjectValidator valObject;

	protected Validator() {
	}

	private BaseValidator<Integer> getIntValidator() {
		Object valObj = ValidatorConfig.get(ConfigValue.INT_VALIDATOR);

		if (valObj != null) {
			return this.<Integer>getValidator(valObj);
		} else {
			if(valInt == null) {
				valInt = new IntegerValidator();
			}
			
			return valInt;
		}
	}
	
	private BaseValidator<Double> getDoubleValidator() {
		Object valObj = ValidatorConfig.get(ConfigValue.DOUBLE_VALIDATOR);

		if (valObj != null) {
			return this.<Double>getValidator(valObj);
		} else {
			if(valDouble == null) {
				valDouble = new DoubleValidator();
			}
			
			return valDouble;
		}
	}
	
	private BaseValidator<String> getStringValidator() {
		Object valObj = ValidatorConfig.get(ConfigValue.STRING_VALIDATOR);

		if (valObj != null) {
			return this.<String>getValidator(valObj);
		} else {
			if(valString == null) {
				valString = new StringValidator();
			}
			
			return valString;
		}
	}
	
	private BaseValidator<Object> getObjectValidator() {
		Object valObj = ValidatorConfig.get(ConfigValue.OBJ_VALIDATOR);

		if (valObj != null) {
			return this.<Object>getValidator(valObj);
		} else {
			if(valObject == null) {
				valObject = new ObjectValidator();
			}
			
			return valObject;
		}
	}
	
	private boolean getExcOnError() {
		Object valObj = ValidatorConfig.get(ConfigValue.EXC_ON_ERROR);
		
		if (valObj != null) {
			try {
				return (boolean) valObj;
			} catch (ClassCastException exc) {
				throw new CastingFailedException(valObj, "boolean");
			}
		} else {
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	private <T> BaseValidator<T> getValidator(Object validatorObject) {
		if (validatorObject == null)
			throw new IllegalArgumentException(
					"'validatorObject' may not be null");

		try {
			return (BaseValidator<T>) validatorObject;
		} catch (ClassCastException exc) {
			throw new CastingFailedException(validatorObject,
					"BaseValidator<T>");
		}
	}

	private boolean doValidateInt(Integer validate, Object[] validatorParams) {
		try {
			getIntValidator().validate(validate, validatorParams);
		} catch (ValidationException exc) {
			if (getExcOnError())
				throw exc;
			else
				return false;
		}

		return true;
	}

	private boolean doValidateDouble(Double validate, Object[] validatorParams) {
		try {
			getDoubleValidator().validate(validate, validatorParams);
		} catch (ValidationException exc) {
			if (getExcOnError())
				throw exc;
			else
				return false;
		}

		return true;
	}

	private boolean doValidateString(String validate, Object[] validatorParams) {
		try {
			getStringValidator().validate(validate, validatorParams);
		} catch (ValidationException exc) {
			if (getExcOnError())
				throw exc;
			else
				return false;
		}

		return true;
	}

	private boolean doValidateObject(Object validate, Object[] validatorParams) {
		try {
			getObjectValidator().validate(validate, validatorParams);
		} catch (ValidationException exc) {
			if (getExcOnError())
				throw exc;
			else
				return false;
		}

		return true;
	}

	private static Validator instance() {
		if (instance == null) {
			synchronized (Validator.class) {
				if (instance == null)
					instance = new Validator();
			}
		}

		return instance;
	}

	public static final boolean validateInt(Integer validate,
			Object... validatorParams) {
		return instance().doValidateInt(validate, validatorParams);
	}

	public static final boolean validateDouble(Double validate,
			Object... validatorParams) {
		return instance().doValidateDouble(validate, validatorParams);
	}

	public static final boolean validateString(String validate,
			Object... validatorParams) {
		return instance().doValidateString(validate, validatorParams);
	}

	public static final boolean validateObject(Object validate,
			Object... validatorParams) {
		return instance().doValidateObject(validate, validatorParams);
	}

}
